package com.gree.flowable.app.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.gree.common.utils.StringUtils;
import com.gree.flowable.app.mapper.FlwExNodesetMapper;
import com.gree.flowable.app.pojo.ex.FlwExNodeset;
import com.gree.flowable.app.pojo.model.QuerySaMO;
import com.gree.flowable.app.pojo.vo.DeployElementVO;
import com.gree.flowable.app.pojo.vo.FlowElementVO;
import com.gree.flowable.app.pojo.vo.ReturnVO;
import com.gree.flowable.app.services.IFlowProcessDiagramGenerator;
import com.gree.flowable.app.services.IFlowableBpmnModelService;
import com.gree.flowable.app.tools.constants.FlowConstant;
import com.gree.flowable.app.tools.constants.ReturnCode;
import com.gree.framework.utils.SecurityUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.collections.CollectionUtils;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.common.engine.impl.util.IoUtil;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.ProcessDefinition;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

@RestController

@Api(tags = "流程定义管理API")
@RequestMapping("/rest/process")
public class IFlowableProcessDefApi {

    @Autowired
    private IFlowableBpmnModelService iFlowableBpmnModelService;

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private FlwExNodesetMapper flwExNodesetMapper;

    @Autowired
    private IFlowProcessDiagramGenerator iFlowProcessDiagramGenerator;

    /**
     * @Title: 查询流程定义所有节点元素 不查询SequenceFlow流元素或者网关元素
     */
    @GetMapping("/{processDefinitionKey}/all-element")
    @ApiOperation("查询流程定义所有节点元素")
    @ApiImplicitParam(name = "processDefinitionKey", value = "流程定义key", dataType = "String")
    public ReturnVO<List<FlowElementVO>> quertModelList(@PathVariable String processDefinitionKey) {
        ReturnVO returnVO = iFlowableBpmnModelService.getFLowElementByProceDefKey(processDefinitionKey);
        return returnVO;
    }

    /**
     * @Title: 根据流程定义ID  获取流程的XML 或者获取流程图
     */
    @GetMapping("/resource/{type}/{processDefinitionKey}")
    @ApiOperation("获取流程的XML 或者获取流程图")
    @ApiImplicitParams({
    @ApiImplicitParam(name = "type", value = "需要查询的类型,xml/image", dataType = "String"),
    @ApiImplicitParam(name = "processDefinitionKey", value = "流程定义Key", dataType = "String")
    })
    @Cacheable(value = FlowConstant.CACHE_PROCESS_IMAGE, key = "'" + FlowConstant.PROCESSINSTANCE_PREFIX + "'+ #processDefinitionKey")
    public void getDefinitionImg(@PathVariable String processDefinitionKey, @PathVariable String type, HttpServletResponse response) {
        try {
            byte[] bytes = null;
            ProcessDefinition pd = repositoryService.createProcessDefinitionQuery().processDefinitionKey(processDefinitionKey).latestVersion().singleResult();
            if (pd != null) {
                if ("xml".equals(type)) {
                    response.setHeader("Content-type", "text/xml;charset=UTF-8");
                    InputStream inputStream = repositoryService.getResourceAsStream(pd.getDeploymentId(), pd.getResourceName());
                    bytes = IoUtil.readInputStream(inputStream, "image inputStream name");
                } else if ("image".equals(type)) {
                    response.setHeader("Content-Type", "image/png");
                    BpmnModel bpmnModel = repositoryService.getBpmnModel(pd.getId());
                    InputStream inputStream = iFlowProcessDiagramGenerator.generateDiagram(bpmnModel);
                    // 将输入流转化为byte便于网络传输
                    bytes = IoUtil.readInputStream(inputStream, "image inputStream name");
                } else {

                }
                response.getOutputStream().write(bytes);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * @Title: 查询所有流程定义
     */
    @GetMapping("/all")
    @ApiOperation("查询所有流程定义")
    public ReturnVO<List<DeployElementVO>> queryDefinition() {
        ReturnVO returnVO = null;
        String systemName = SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString();
        if(systemName.equals("admin")){
            systemName = null;
        }
        List<ProcessDefinition> list = null;

        if (StringUtils.isEmpty(systemName)){
            list = repositoryService.createProcessDefinitionQuery().latestVersion().list();
        }else {
            list = repositoryService.createProcessDefinitionQuery().processDefinitionTenantId(systemName).latestVersion().list();
        }

        List<DeployElementVO> result = new ArrayList<>();
        list.forEach(processDefinition -> {
            DeployElementVO vo = new DeployElementVO();
            vo.setId(processDefinition.getId());
            vo.setName(processDefinition.getName());
            vo.setKey(processDefinition.getKey());
            vo.setCategory(processDefinition.getCategory());
            vo.setTenantId(processDefinition.getTenantId());
            vo.setVersion(processDefinition.getVersion());
            result.add(vo);
        });

        returnVO = new ReturnVO(ReturnCode.SUCCESS, "OK", result);
        return returnVO;
    }

    /**
     * @Title: 删除流程定义
     */
    @DeleteMapping("/bykey/{processDefinitionKey}")
    @ApiOperation("删除流程定义")
    @ApiImplicitParam(name = "processDefinitionKey", value = "流程定义Key", dataType = "String")
    public ReturnVO deleteDeployment(@PathVariable String processDefinitionKey) {
        ReturnVO returnVO = null;
        try {
            ProcessDefinition pd = repositoryService.createProcessDefinitionQuery().processDefinitionKey(processDefinitionKey).latestVersion().singleResult();
            // true  表示删除所有关系
            repositoryService.deleteDeployment(pd.getDeploymentId(), true);
            try {
                QueryWrapper<FlwExNodeset> wrapper = new QueryWrapper<>();
                wrapper.lambda().eq(FlwExNodeset::getProcessKey, pd.getKey()).eq(FlwExNodeset::getVers, pd.getVersion());
                flwExNodesetMapper.delete(wrapper);
                returnVO = new ReturnVO(ReturnCode.SUCCESS,"删除成功！");
            }catch (Exception e){
                  e.printStackTrace();
                returnVO = new ReturnVO(ReturnCode.SUCCESS,"已删除流程定义，但流程设置表删除失败！");
            }
        }catch (Exception e){
            e.printStackTrace();
            returnVO = new ReturnVO(ReturnCode.FAIL,e.getMessage());
        }
        return returnVO;
    }

    /**
     * @Title: 激活或者挂起流程定义
     * @param: id-流程实例ID
     *         state-需要改变的状态
     * 查看act_re_procdef表 suspension_state == 1  激活状态
     *                     suspension_state == 2  挂起状态
     */
    @PutMapping("/suspense")
    @ApiOperation("激活或者挂起流程定义")
    @ApiImplicitParam(name = "mo", value = "挂起请求参数实体类", dataType = "QuerySaMO")
    public ReturnVO saDefinitionById(@RequestBody QuerySaMO mo) {
        ReturnVO returnVO = null;
        Integer suspensionState = mo.getSuspensionState();
        if(suspensionState == 2){
            // true  相关表全部挂起 第三个参数 时间
            repositoryService.suspendProcessDefinitionByKey(mo.getKey(), true, null);
            returnVO = new ReturnVO(ReturnCode.SUCCESS,"挂起成功！");
        }else {
            repositoryService.activateProcessDefinitionByKey(mo.getKey(), true, null);
            returnVO = new ReturnVO(ReturnCode.SUCCESS,"激活成功！");
        }
        return returnVO;
    }

    @GetMapping("/{processDefinitionKey}/node-role")
    public ReturnVO<List<FlwExNodeset>> queryNodeList(@PathVariable String processDefinitionKey) {
        ReturnVO returnVO = null;
        List<FlwExNodeset> maxVersionList = flwExNodesetMapper.getMaxVersionList(processDefinitionKey);
        if (CollectionUtils.isNotEmpty(maxVersionList)){
            returnVO = new ReturnVO(ReturnCode.SUCCESS, "ok", maxVersionList);
        }else{
            returnVO = new ReturnVO(ReturnCode.FAIL, "该流程还未发布，未查询其节点信息!");
        }
        return returnVO;
    }

    @PostMapping("/edit-node")
    public ReturnVO editNode(@RequestBody FlwExNodeset params){
        ReturnVO returnVO = null;
        QueryWrapper<FlwExNodeset> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(FlwExNodeset::getProcessKey, params.getProcessKey())
                .eq(FlwExNodeset::getElementId, params.getElementId())
                .eq(FlwExNodeset::getVers, params.getVers());
        int update = flwExNodesetMapper.update(params, wrapper);
        if (update>0){
            returnVO = new ReturnVO(ReturnCode.SUCCESS, "更新成功");
        }else {
            returnVO = new ReturnVO(ReturnCode.FAIL, "更新失败");
        }
        return returnVO;
    }


}
